﻿using System;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using System.Globalization;
using XCaptcha;

namespace Eacademy.Library.Validations
{
    public class EmailAttribute : RegexAttribute
    {
        public EmailAttribute()
            : base(@"^[\w-\.]{1,}\@([\w]{1,}\.){1,}[a-z]{2,4}$", RegexOptions.IgnoreCase)
        {
        }
    }

    public class RegexAttribute : ValidationAttribute
    {
        public string Pattern { get; set; }
        public RegexOptions Options { get; set; }

        public RegexAttribute(string pattern)
            : this(pattern, RegexOptions.None)
        {
        }

        public RegexAttribute(string pattern, RegexOptions options)
        {
            this.Pattern = pattern;
            this.Options = options;
        }

        public override bool IsValid(object value)
        {
            //null or empty is <EM>not</EM> invalid
            var str = (string)value;
            if (string.IsNullOrEmpty(str))
                return true;

            return new Regex(this.Pattern, this.Options).IsMatch(str);
        }
    }

    public sealed class StringRangeAttribute : ValidationAttribute
    {
        public int MinLength { get; set; }
        public int MaxLength { get; set; }

        public StringRangeAttribute(int minLength, int maxLength)
        {
            this.MinLength = (minLength < 0) ? 0 : minLength;
            this.MaxLength = (maxLength < 0) ? 0 : maxLength;
        }

        public override bool IsValid(object value)
        {
            //null or empty is <em>not</em> invalid
            var str = (string)value;
            if (string.IsNullOrEmpty(str))
                return true;

            return (str.Length >= this.MinLength && str.Length <= this.MaxLength);
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";

        private readonly int _minCharacters = 6;

        public ValidatePasswordLengthAttribute()
            : base(_defaultErrorMessage)
        {
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                name, _minCharacters);
        }

        public override bool IsValid(object value)
        {
            string valueAsString = value as string;
            return (valueAsString != null && valueAsString.Length >= _minCharacters);
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class MustMatchPropertyAttribute : ValidationAttribute
    {
        private const string DefaultErrorMessage = "Must match {0}";
        private readonly object _typeId = new object();

        public MustMatchPropertyAttribute(string propertyToMatch)
            : base(DefaultErrorMessage)
        {
            PropertyToMatch = propertyToMatch;
        }

        public string PropertyToMatch { get; private set; }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, PropertyToMatch);
        }

        public override bool IsValid(object value)
        {
            // we don't have enough information here to be able to validate against another field
            // we need the DataAnnotationsMustMatchPropertyValidator adapter to be registered
            throw new Exception("MustMatchPropetyAttribute requires the DataAnnotationsMustMatchPropertyValidator adapter to be registered"); // TODO – make this a typed exception :-)
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class MustMatchObjectPropertyAttribute : ValidationAttribute
    {
        private const string DefaultErrorMessage = "Must match {0}";
        private readonly object _typeId = new object();

        public MustMatchObjectPropertyAttribute(string propertyToMatch)
            : base(DefaultErrorMessage)
        {
            PropertyToMatch = propertyToMatch;
        }

        public string PropertyToMatch { get; private set; }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, PropertyToMatch);
        }

        public override bool IsValid(object value)
        {
            // we don't have enough information here to be able to validate against another field
            // we need the DataAnnotationsMustMatchObjectPropertyValidator adapter to be registered
            throw new Exception("MustMatchObjectPropertyAttribute requires the DataAnnotationsMustMatchObjectPropertyValidator adapter to be registered"); // TODO – make this a typed exception :-)
        }
    }

    // TODO: Convert to property level validation *make this work
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class MustMatchEncryptedSolutionAttribute : ValidationAttribute
    {

        private readonly object _typeId = new object();

        public MustMatchEncryptedSolutionAttribute(string encryptedSolutionProperty, string secretKey)
        {
            EncryptedSolutionProperty = encryptedSolutionProperty;
            SecretKey = secretKey;
            EncryptionProvider = new EncryptionProvider();

        }

        public string SecretKey { get; private set; }
        public string EncryptedSolutionProperty { get; private set; }

        public EncryptionProvider EncryptionProvider { get; set; }
        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }


        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,EncryptedSolutionProperty);
        }


        public override bool IsValid(object value)
        {
            // we don't have enough information here to be able to validate against another field
            // we need the DataAnnotationsMustMatchEncryptedSolutionValidator adapter to be registered
            throw new Exception("MustMatchEncryptedSolutionAttribute requires the DataAnnotationsMustMatchEncryptedSolutionValidator adapter to be registered"); // TODO – make this a typed exception :-)
        }

    }

}